package file

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"quipus/internal/rag/filesig"
	"regexp"
	"strings"
	"unicode/utf8"

	"github.com/ledongthuc/pdf"
)

type TxtReader struct {
	filepath string
	docId    string
	rsId     int32
}

type PdfReader struct {
	filepath string
	docId    string
	rsId     int32
}

type Extract interface {
	ExtractContent(extract_func func(string, string, int32, bool) error) error
}

func NewExtractor(docId string, rsid int32, filepath string) (Extract, error) {
	isTxt, err := IsTxt(filepath)
	if err != nil {
		return nil, err
	}

	if isTxt {
		return &TxtReader{filepath: filepath, rsId: rsid, docId: docId}, nil
	}

	pdfpath, err := usePdf(filepath)
	if err != nil {
		return nil, err
	}

	return &PdfReader{filepath: pdfpath, rsId: rsid, docId: docId}, nil
}

func usePdf(input string) (string, error) {
	buff, err := os.Open(input)

	if err != nil {
		return "", err
	}

	defer func() {
		buff.Close()
	}()

	valid := filesig.IsPdf(buff)
	if valid {
		return input, nil
	}

	if filesig.IsOneOf(buff, filesig.IsMsOffice, filesig.IsOldMsOffice) {
		return Convert2Pdf(input, "./")
	}
	return "", fmt.Errorf("file is not supported")
}

func (r *TxtReader) ExtractContent(extract_func func(string, string, int32, bool) error) error {
	file, err := os.Open(r.filepath)
	if err != nil {
		fmt.Println("open file err=", err)
	}

	defer file.Close()
	reader := bufio.NewReader(file)

	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF {
			extract_func(r.docId, "", r.rsId, true)
			break
		}
		extract_func(r.docId, str, r.rsId, false)
	}

	return nil
}

func (r *PdfReader) ExtractContent(extract_func func(string, string, int32, bool) error) error {
	f, rd, err := pdf.Open(r.filepath)

	defer func() {
		_ = f.Close()
	}()

	if err != nil {
		return err
	}
	totalPage := rd.NumPage()
	for pageIndex := 1; pageIndex <= totalPage; pageIndex++ {
		p := rd.Page(pageIndex)
		if p.V.IsNull() {
			continue
		}

		content := p.Content()
		var sb strings.Builder
		for _, text := range content.Text {
			cleanedText := cleanText(text.S)
			sb.WriteString(cleanedText)
		}

		filteredText := filterNonUTF8(sb.String())
		extract_func(r.docId, filteredText, r.rsId, false)
	}
	extract_func(r.docId, "", r.rsId, true)
	return nil
}

func cleanText(text string) string {
	// 1. 过滤非文本字符（保留字母、数字、标点符号和常见符号）
	re := regexp.MustCompile(`[^\p{L}\p{N}\p{P}\p{S}\s]`)
	cleaned := re.ReplaceAllString(text, "")

	return cleaned
}

func filterNonUTF8(text string) string {
	var result strings.Builder
	for _, r := range text {
		// 检查字符是否为有效的 UTF-8 字符
		if r != utf8.RuneError {
			result.WriteRune(r)
		}
	}
	return result.String()
}

func IsTxt(filepath string) (bool, error) {
	cmd := exec.Command("file", filepath)
	bb, err := cmd.Output()
	if err != nil {
		return false, err
	}

	txtstr := string(bb)
	if strings.Contains(txtstr, "text") {
		return true, nil
	}
	return false, nil
}

func IsFileExists(filename string) bool {
	_, err := os.Stat(filename)
	if err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

func Convert2Pdf(input string, outDir string) (string, error) {
	fname := filepath.Base(input)
	ext := filepath.Ext(fname)
	fileName := strings.TrimSuffix(fname, ext) + ".pdf"
	outPath := filepath.Join(outDir, fileName)
	if IsFileExists(outPath) {
		return outPath, nil
	}

	cmd := exec.Command("soffice", "--headless", "--convert-to", "pdf", input, "--outdir", outDir)
	bb, err := cmd.Output()
	if err != nil {
		return "", err
	}

	retstr := string(bb)
	re := regexp.MustCompile(`->\s*([^\s]+)`)
	matches := re.FindStringSubmatch(retstr)

	if len(matches) > 1 {
		filePath := matches[1]
		return filePath, nil
	} else {
		return "", fmt.Errorf("没有匹配到文件路径")
	}
}
